package com.ruoyi.iot.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.ruoyi.iot.domain.IotRule;
import com.ruoyi.iot.domain.IotRulesAction;
import com.ruoyi.iot.domain.IotRulesTrigger;
import com.ruoyi.iot.domain.IotRulesTriggerTerm;
import com.ruoyi.iot.domain.vo.KuiperSqlObject;
import com.ruoyi.iot.enums.ActionTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.StringJoiner;
import java.util.stream.Collectors;

/**
 * eKuiper 规则生成器服务
 * 负责根据物模型规则、触发器和动作，生成 eKuiper 可执行的 SQL 和 actions JSON。
 *
 * @author Howe
 * @since 2025/7/17
 */
@Service
@Slf4j
public class EKuiperRuleGeneratorService {

    /**
     * MQTT 主题前缀-发送设备命令
     */
    private static final String DEVICE_SERVICE_TOPIC = "/iot/rule/device/service/";
    /**
     * MQTT 主题前缀-上报告警
     */
    private static final String DEVICE_ALERT_TOPIC = "/iot/rule/device/alert/";
    /**
     * MQTT 主题前缀-发送通知
     */
    private static final String DEVICE_NOTICE_TOPIC = "/iot/rule/device/notice/";
    /**
     * MQTT 服务器地址
     */
    private static final String MQTT_HOST = "tcp://47.120.66.54:1883";

    private static final String START_TEMPLATE = "{\"sql\":\"CREATE STREAM {} ( ";
    private static final String END_TEMPLATE = " ) WITH ( datasource = \\\"{}\\\", FORMAT = \\\"{}\\\", KEY = \\\"{}\\\")\"}";


    /**
     * 属性类型到 eKuiper SQL 类型的映射
     *
     * @param attributeType 属性类型（如:STRING、INTEGER、FLOAT、BOOLEAN、DATE、TIMESTAMP、ARRAY、OBJECT）
     * @return eKuiper SQL 类型
     * @author Howe
     * @since 2025/7/17
     **/
    public String mapAttributeTypeToEKuiperType(String attributeType) {
        switch (attributeType) {
            case "STRING":
                return "STRING";
            case "INTEGER":
                return "BIGINT";
            case "FLOAT":
                return "FLOAT";
            case "BOOLEAN":
                return "BOOLEAN";
            case "DATE":
                // 或 BIGINT，视实际数据格式
                return "STRING";
            case "TIMESTAMP":
                return "BIGINT";
            case "ARRAY":
                return "ARRAY";
            case "OBJECT":
                return "OBJECT";
            default:
                throw new IllegalArgumentException("不支持的属性类型: " + attributeType);
        }
    }

    /**
     * 生成 eKuiper 规则 SQL。
     *
     * @param rule 规则对象，包含触发器等信息。
     * @return 生成的 eKuiper SQL 语句。
     * @author Howe
     * @since 2025/7/17
     **/
    public String generateEKuiperSql(IotRule rule) {
        log.info("rule====>{}", JSONUtil.toJsonStr(rule));
        if (rule == null || rule.getIotRulesTrigger() == null) {
            throw new IllegalArgumentException("规则或触发器不能为空");
        }
        IotRulesTrigger trigger = rule.getIotRulesTrigger();
        String fromClause = StrUtil.format("FROM mqtt_topic_{}", rule.getRuleId());
        String whereClause = buildWhereClause(trigger, rule);
        return StrUtil.format("SELECT *  {} WHERE {}", fromClause, whereClause);
    }


    /**
     * 将 List<IotRulesAction> 转换为 eKuiper 规则 actions 的对象数组
     *
     * @param actionList 动作列表
     * @return actions 对象数组（每个元素为 {type: params}）
     * @author Howe
     * @since 2025/7/17
     **/
    public List<Map<String, Object>> generateEKuiperActionsList(List<IotRulesAction> actionList) {
        List<Map<String, Object>> ekuiperActions = new java.util.ArrayList<>();
        
        // 默认添加日志动作
        ekuiperActions.add(java.util.Collections.singletonMap("log", new java.util.HashMap<>()));
        
        // 如果动作列表为空，则只返回日志动作
        if (CollUtil.isEmpty(actionList)) {
            return ekuiperActions;
        }
        
        // 处理每个动作
        for (IotRulesAction action : actionList) {
            Map<String, Object> mqttAction = createMqttAction(action);
            if (!mqttAction.isEmpty()) {
                ekuiperActions.add(java.util.Collections.singletonMap("mqtt", mqttAction));
            }
        }
        
        return ekuiperActions;
    }
    
    /**
     * 根据动作类型创建MQTT动作参数
     *
     * @param action 规则动作
     * @return MQTT动作参数
     * @author Howe
     * @since 2025/7/17
     **/
    private Map<String, Object> createMqttAction(IotRulesAction action) {
        Map<String, Object> params = new java.util.HashMap<>();
        String type = action.getType();
        String clientId = "ruleEKuiper" + System.currentTimeMillis();
        
        // 设置通用参数
        params.put("server", MQTT_HOST);
        params.put("sendSingle", true);
        params.put("clientId", clientId);
        
        // 根据动作类型设置特定参数
        Map<String, Object> dataTemplate = new java.util.LinkedHashMap<>();
        
        if (ActionTypeEnum.DEVICE_OUTPUT.getType().equals(type)) {
            params.put("topic", DEVICE_SERVICE_TOPIC);
            dataTemplate.put("deviceId", "{{.deviceId}}");
            dataTemplate.put("serviceId", action.getServiceMethod());
            dataTemplate.put("serviceName", action.getServiceMethod());
            dataTemplate.put("serviceParams", action.getServiceParams());
        } else if (ActionTypeEnum.REPORT_ERROR.getType().equals(type)) {
            params.put("topic", DEVICE_ALERT_TOPIC);
            dataTemplate.put("deviceId", "{{.data.deviceId}}");
            dataTemplate.put("alertId", action.getServiceMethod());
            dataTemplate.put("alertName", action.getServiceMethodName());
        } else if (ActionTypeEnum.REPORT_NOTICE.getType().equals(type)) {
            params.put("topic", DEVICE_NOTICE_TOPIC);
            dataTemplate.put("deviceId", "{{.data.deviceId}}");
            dataTemplate.put("noticeId", action.getServiceMethod());
            dataTemplate.put("noticeName", action.getServiceMethodName());
        } else {
            // 不支持的动作类型
            return Collections.emptyMap();
        }
        
        params.put("dataTemplate", dataTemplate);
        return params;
    }

    /**
     * 构建 SQL 的 WHERE 条件子句。
     * 该方法会结合触发项条件和时间条件来构建完整的 WHERE 子句。
     * 同时会添加产品ID和设备ID的过滤条件。
     *
     * @param trigger 规则触发器，包含触发项和触发条件。
     * @param rule    规则对象，用于获取时间相关的生效条件。
     * @return 构建好的 WHERE 条件 SQL 片段。
     * @author Howe
     * @since 2025/7/17
     **/
    private String buildWhereClause(IotRulesTrigger trigger, IotRule rule) {
        List<IotRulesTriggerTerm> terms = trigger.getTriggerTerms();
        if (CollUtil.isEmpty(terms)) {
            throw new IllegalArgumentException("触发项不能为空");
        }

        // 构造触发项条件
        String termConditions = terms.stream()
                .map(this::buildTermCondition)
                .collect(Collectors.joining(" " + trigger.getTriggerCondition().toUpperCase() + " "));

        // 获取第一个触发项的产品ID和设备ID（假设所有触发项都属于同一产品和设备）
        IotRulesTriggerTerm firstTerm = terms.get(0);
        Long productId = firstTerm.getProductId();
        Long deviceId = firstTerm.getDeviceId();
        
        // 添加产品ID和设备ID条件
        StringBuilder whereBuilder = new StringBuilder();
        if (productId != null) {
            whereBuilder.append("data.productId = ").append(productId);
        }
        
        if (deviceId != null) {
            if (whereBuilder.length() > 0) {
                whereBuilder.append(" AND ");
            }
            whereBuilder.append("data.deviceId = ").append(deviceId);
        }
        
        // 合并所有条件
        if (whereBuilder.length() > 0) {
            if (StrUtil.isNotBlank(termConditions)) {
                whereBuilder.append(" AND (").append(termConditions).append(")");
            }
        } else {
            whereBuilder.append(termConditions);
        }

        // 构造时间条件
        String timeCondition = buildTimeCondition(rule);
        if (StrUtil.isNotBlank(timeCondition)) {
            return whereBuilder.length() == 0 ? timeCondition
                : StrUtil.format("{} AND {}", whereBuilder, timeCondition);
        }

        return whereBuilder.toString();
    }

    /**
     * 根据单个触发项构建 SQL 条件表达式。
     *
     * @param term 触发项定义。
     * @return 单个条件的 SQL 表达式字符串。
     * @author Howe
     * @since 2025/7/17
     **/
    private String buildTermCondition(IotRulesTriggerTerm term) {
        String field = "data." + term.getAttributeCode();
        String operator = convertOperator(term.getComparisonOperator());

        switch (term.getComparisonOperator()) {
            case "range":
                return StrUtil.format("{} BETWEEN {} AND {}", field, term.getValueMin(), term.getValueMax());
            case "contain":
                return StrUtil.format("{} IN ({})", field, term.getValueList());
            default:
                return StrUtil.format("{} {} {}", field, operator, term.getValueMax());
        }
    }

    /**
     * 将自定义的比较操作符字符串转换为标准 SQL 运算符。
     *
     * @param comparisonOperator 自定义比较操作符（如 "equal", "less_than"）。
     * @return 对应的 SQL 运算符（如 "=", "<"）。
     * @author Howe
     * @since 2025/7/17
     **/
    private String convertOperator(String comparisonOperator) {
        switch (comparisonOperator) {
            case "equal":
                return "=";
            case "less_than":
                return "<";
            case "less_than_or_equal_to":
                return "<=";
            case "greater_than":
                return ">";
            case "greater_than_or_equal_to":
                return ">=";
            default:
                throw new IllegalArgumentException("不支持的比较运算符: " + comparisonOperator);
        }
    }

    /**
     * 根据规则中的生效时间配置，构建时间相关的 SQL 条件。
     * 支持按特定时间段、星期周期和日期范围进行过滤。
     *
     * @param rule 包含生效时间配置的规则对象。
     * @return 时间相关的 SQL 条件表达式；如果规则不是指定时间生效，则返回空字符串。
     * @author Howe
     * @since 2025/7/17
     **/
    private String buildTimeCondition(IotRule rule) {
        if (!"appoint".equals(rule.getTakeEffectType())) {
            return "";
        }

        StringBuilder condition = new StringBuilder();

        // 时间范围
        if (StrUtil.isNotBlank(rule.getTakeEffectTime())) {
            String[] times = rule.getTakeEffectTime().split("至");
            if (times.length == 2) {
                condition.append(StrUtil.format("HOUR(NOW())*60 + MINUTE(NOW()) BETWEEN {}*60 + {} AND {}*60 + {}",
                        times[0].split(":")[0], times[0].split(":")[1],
                        times[1].split(":")[0], times[1].split(":")[1]));
            }
        }

        // 周期
        if (StrUtil.isNotBlank(rule.getTakeEffectWeeks())) {
            List<String> weekList = StrUtil.split(rule.getTakeEffectWeeks(), ",", 0, true, true);
            if (CollUtil.isNotEmpty(weekList)) {
                String weeks = weekList.stream()
                        .map(this::convertWeekToNumber)
                        .collect(Collectors.joining(","));
                if (condition.length() > 0) {
                    condition.append(" AND ");
                }
                condition.append(StrUtil.format("DAYOFWEEK(NOW()) IN ({})", weeks));
            }
        }

        // 日期范围
        if (StrUtil.isNotBlank(rule.getEffectiveDate())) {
            String[] dates = rule.getEffectiveDate().split("至");
            if (dates.length == 2) {
                if (condition.length() > 0) {
                    condition.append(" AND ");
                }
                condition.append(StrUtil.format("DATE(NOW()) BETWEEN '{}' AND '{}'", dates[0], dates[1]));
            }
        }

        return condition.toString();
    }

    /**
     * 将中文星期名称转换为 eKuiper 函数期望的数字（1=周日, 2=周一, ..., 7=周六）。
     *
     * @param week 中文星期名称（如 "周一"）。
     * @return 对应的数字字符串。
     * @author Howe
     * @since 2025/7/17
     **/
    private String convertWeekToNumber(String week) {
        if (StrUtil.isBlank(week)) {
            throw new IllegalArgumentException("星期值不能为空");
        }
        switch (week.trim()) {
            case "周一":
                return "2";
            case "周二":
                return "3";
            case "周三":
                return "4";
            case "周四":
                return "5";
            case "周五":
                return "6";
            case "周六":
                return "7";
            case "周日":
                return "1";
            default:
                throw new IllegalArgumentException("无效的星期: " + week);
        }
    }


    /**
     * 生成创建流语句
     *
     * @param kuiperSqlObject 流创建对象
     * @return 流创建SQL
     * @author Howe
     * @since 2025/7/19 11:46
     **/
    public String getCreateStreamSQL(KuiperSqlObject kuiperSqlObject) {
        StringJoiner stringJoiner = new StringJoiner(",",
                StrUtil.format(START_TEMPLATE, kuiperSqlObject.getStreamName()),
                StrUtil.format(END_TEMPLATE, kuiperSqlObject.getDataSource() != null ?
                                kuiperSqlObject.getDataSource() : kuiperSqlObject.getTopicTitle(), kuiperSqlObject.getFormat(),
                        kuiperSqlObject.getKey()));
        Map<String, String> attributeMap = kuiperSqlObject.getAttributeMap();
        attributeMap.forEach((key, value) -> {
            stringJoiner.add(key + " " + value);
        });
        return stringJoiner.toString();
    }
}